import re

__author__ = 'N Tech2'



""" Giovanni's MicroCompiler Demo

<program>       ::=     { <id> ; <vars > <stmtlist>  }
<vars>          ::=     V { <id> ; }                       % DECLARATIONS

<stmtlist>      ::=     <stmt> { ; <stmt> }
<stmt>          ::=     P <id>  |  <id> = <expr>

<expr>          ::=     <factor> {  (+ | -)   <factor> }   % No precedence
<factor>                ::=     ID | Number

"""

import sys

#===============================================================================
# # Lexical Analyser - the "Scanner" - Define the TokenTypes
#===============================================================================

tokenNames =\
  [ "unknownToken","programsym", "idsym", "assignsym", "leftbrace", "rightbrace",
    "varsym", "semicolon", "whilesym", "leftbracket", "rightbracket",
    "printsym", "ifsym", "equals", "comment", "lessthan", "greaterthan",
    "number", "plus", "minus", "endsym" ,"elsesym","declaresym","forsym",
    "andsym","elifsym","continuesym","nonesym","factorial",
    "leftsquarebrace","rightsquarebrace","lessthanequal","colon","comma",
    "dot"," greaterthanequal","jumpeq","jumpne","andop","orop","readint","dollar",
    "writeint","readch","writech","mpy","div","quote ","stringsym","arraysym",
    "readsym","breaksym","breaksym","endfile"]

# define the tokens as variables
unknownToken,programsym,idsym, assignsym, leftbrace, rightbrace,\
    varsym, semicolon, whilesym, leftbracket, rightbracket,\
    printsym, ifsym, equals, comment, lessthan, greaterthan,\
    number, plus, minus,elsesym,declaresym,forsym,andsym,elifsym,\
    continuesym,factorial,nonesym,dollar,leftsquarebrace,rightsquarebrace,\
    lessthanequal,colon,comma,dot, greaterthanequal,jumpeq,jumpne,\
    andop,orop,endfile,readint,writeint,readch,writech,\
    mpy,div,quote,stringsym,arraysym,readsym,breaksym,\
    endfile = range(0, len(tokenNames))

#===============================================================================
class symbol:
#===============================================================================
    name      = None   # String representation
    token     = None;  # Corresponding token
    address   = 0;     # For variables, their runtime address
    value     = None   # on defined for numbers

    def __init__(self, name, token, value = 0):
        self.name    = name
        self.token   = token
        self.address = 0   # for identifiers, their address
        self.value   = value   # for numbers, their value


symtbl = {}; # The Symbol Table, a dictionary of symbols indexed by name

#======================================================================
# GLOBALS for output of Lexical Analyser
# Set by each call to GeToken()

token = None;   # A symbol

line      = "\n" # the complex source file as a string
charIndex = 0    # the position in the source

linenumber= 0    # The current  number

EOF = chr(0)
#======================================================================
# Run Time Code Generation Variables

varptr  = 500   # Arbitrary base address of memory for variables
codeptr = 10    # Address to output next instruction (for code)


#===============================================================================
#                    Symbol Table Management Routines
#===============================================================================

#===============================================================================
def addToSymTbl (name, token):
#===============================================================================
    symtbl[name] = symbol(name, token)  # Add a new symbol to the dictionary
    return symtbl[name]


#===============================================================================
def lookup (thisName):
#===============================================================================
    "# Find a given identifier in the Symbol Table, Return the symtbl entry"
    if symtbl.has_key(thisName):

     # thisName has been seen before
        return symtbl[thisName]   # return the symtbl entry
    else: return None


#===============================================================================
def initSymTbl():
#===============================================================================
    "# Initialise Symbol Table, and preload reserved words"
    addToSymTbl('var',   varsym)
    addToSymTbl('program',programsym)
    addToSymTbl('break', breaksym)
    addToSymTbl('declare', declaresym)
    addToSymTbl('while', whilesym) # WHILE
    addToSymTbl('print', printsym) # PRINT
    addToSymTbl('else',   elsesym)
    addToSymTbl('elif',   elifsym)
    addToSymTbl('if',   ifsym)
    addToSymTbl('for',    forsym)
    addToSymTbl('and',    andsym)
    addToSymTbl('None',    nonesym)
    addToSymTbl("hello world", stringsym)
    addToSymTbl('continue',  continuesym)
    addToSymTbl('readint', readint) #READINT
    addToSymTbl('writeint', writeint) #READINT
    addToSymTbl('readch', readch) #READINT
    addToSymTbl('writech', writech)
    addToSymTbl('read', readsym)#READINT



    # Now add symbols - NB only single character symbols are here
    #                      multicharacter one like ">=" are still to do
    addToSymTbl( '=', assignsym)
    addToSymTbl( '#', comment)
    addToSymTbl( '<', lessthan)
    addToSymTbl( '>', greaterthan)
    addToSymTbl( '{', leftbrace )
    addToSymTbl( '}', rightbrace)
    addToSymTbl( '(', leftbracket)
    addToSymTbl( ')', rightbracket)
    addToSymTbl( '+', plus )
    addToSymTbl( '-', minus)
    addToSymTbl( ';', semicolon)
    addToSymTbl( '-', minus)
    addToSymTbl( '!', factorial)
    addToSymTbl( '*', mpy)
    addToSymTbl( '/', div)
    addToSymTbl( '$', dollar)
    addToSymTbl( '[', leftsquarebrace)
    addToSymTbl( ']', rightsquarebrace)
    addToSymTbl( ':', colon)
    addToSymTbl( ',', comma)
    addToSymTbl( '.', dot)
    addToSymTbl( '<=', lessthanequal)
    addToSymTbl( '>=', greaterthanequal)
    addToSymTbl( '==', jumpeq)
    addToSymTbl( '!=', jumpne)
    addToSymTbl( '&&', andop)
    addToSymTbl( '||', orop)
    addToSymTbl( '"',quote)

    addToSymTbl( EOF, endfile)

#===============================================================================
#                       Scanner Diagnostics
#===============================================================================
def printToken(tok):
#===============================================================================
    " - display the specified token."
    if tok.token == idsym:
       print "Token = %10s,  %-10s  adr = %3d" %\
             (tokenNames[tok.token], tok.name, tok.address)
    elif tok.token == number:
       print "Token = %10s  %d" % (tokenNames[tok.token], tok.value)
    else:
       print "Token = %10s" %  tokenNames[tok.token]

#==============================?=================================================
def dumpSymTbl():
#===============================================================================
    """ Dump all the tokens in the symboltable """
    print(" *** Symbol Table ***")
    for name in symtbl.keys():  # keys is a list of the names (printable form of tokens)
        tok = symtbl[name]
        if tok.token == idsym:
            printToken(tok)

#===============================================================================
def getch():
#===============================================================================
    global line
    global charIndex
    global linenumber

    while True:
        if charIndex < len(line):        # See if used up current line
            ch = line[charIndex]        # if not, get character
            charIndex = charIndex+1     # & move pointer for next time
        else:
            # line = f.readline()
            # if  line == "": line = EOF
            print "--> ",
            line = raw_input() +"\n" # read new line, adding \n so it's like f.readline()
            charIndex = 0       # & reset character pointer
            linenumber = linenumber + 1
            continue

        if ch == "\n":
            ch = " "                # A newline is a token separator

        if ch == '?':
            dumpSymTbl()
            continue
        return ch

#===============================================================================
def ungetch():
#===============================================================================
    """ Unget the next character peeked at when building variables, numbers
        and when distinguishing between >= and > ...
    """

    global charIndex;
    charIndex = charIndex-1;

#===============================================================================
def getoken():
#===============================================================================
    """ GETOKEN - Put next token into the global TOKEN """
    global token
    x = ""
    ch = getch()             # skip whitespace
    while ch in ["\t", " ", "\n", "#"]:
        ch = getch()

    # If ch is alphabetic then this is start of reserved word or an identifier
    if ch.isalpha():  # Letter therefore it's either identifier or reserved word
        name = ""
        while ch.isalpha() or ch.isdigit() or ch == "_":
            name = name + ch
            ch = getch()

        ungetch()                             # let terminator be used next time

        token = lookup(name)                  # See if token's known
        if token is None:                     # if not
            token = addToSymTbl(name, idsym)  #   add as 'idsym'-IDENTIFIER

        return   # we've set token.token to either id or tokentype of reserved word

    #---------------------------------------------------------------------
    # If it's numeric return TOKEN=number & token.value = binary value
    elif ch.isdigit():

        # In the real version, build number and don't forget to end with ungetch()
        while(ch.isdigit() == True):
            x = x + ch
            ch = getch()

        token = symbol(x, number, value = int(x)) # simplistic SINGLE DIGIT Ascii to Binary
        return

    #---------------------------------------------------------------------
    # Single character tokens
    elif ch in ["=",  "#",  "<",  ">",  "{", "}", "(", ")", "[","]", "+" , "-", "*", "/", ";","!", "==",",","<=",">=",'"',":", EOF]:
        token = lookup(ch)  # preloaded with appropriate token
    else:
        print "Unknown character -->%s<- decimal %d" % (ch,ord(ch))
        sys.exit(1) # Abort Compilation

#======================================================================
# THE GRAMMAR

# <program>       ::=     { <id> ; <vars > <stmtlist>  }
# <vars>          ::=     var { <id> ; }                    % DECLARATIONS
# <array>         ::=     [<expr>]

# <stmtlist>      ::=     <stmt> { ; <stmt> }
# <stmt>          ::=     print <id>
#                         <id> = <expr>

# <expr>          ::=     <factor> {  (+ | - | * | /)   <factor> }   % No precedence
# <factor>        ::=     [+/-] ID | Number

#======================================================================
#                  Centralised Parser Error Message handler
#======================================================================

#===============================================================================
def error (msg):
#===============================================================================
    print line
    print "-" * charIndex,"^"
    print("Error on  %d - %s\n" % (number, msg))
    printToken(token)
    print("\n")


#===============================================================================
def emit (memadr, opcode, parameter):
#===============================================================================
    """EMIT CODE - Emit a  of code with a Parameter
    if first arg is zero - (the memory address), use and incr CODEPTR"""
    global codeptr

    if (memadr == 0):
        memadr = codeptr
        codeptr = codeptr+1
    print "%6d  %-8s %-7s" % (memadr, opcode, parameter)

#======================================================================
# VARIABLE DECLARATIONS
#   <vars>              ::=     V { <id> ; }

#===============================================================================
def vars() :
#===============================================================================

    ta = 0
    tn = []
    global varptr;

    getoken();

    if token.token == idsym:
        ta = token.address
        tn = token.name
        getoken()

        if token.token == leftsquarebrace:

            getoken()

            if token.token == number:

                getoken()

                if token.token == rightsquarebrace:
                    getoken()
                else:
                    error("] expected")

            else: error("number expected")
        else:

            print
            if ta != 0:
                print("%c already declared\n", token.name);

                assignStmt()
            else:
                symtbl[tn].address = varptr;
                varptr = varptr +1

    if token.token == assignsym:
        getoken()

        expression()

    if token.token == comma:
        vars()
    elif token.token == semicolon : getoken()

    else: error("comma or semicolon expected in declaration")

#===============================================================================
def array():
#===============================================================================
    global array;
    getoken()
    while (token.token == number):
        print("#array address")
        emit(0, "constant", token.value)
        emit(0, "constant", token.value)
        emit(0, "constant", token.value)
        emit(0, "constant", token.value)
        emit(0, "constant", 4)
        getoken();

    if token.token == rightsquarebrace:getoken();
    if token.token == assignsym:getoken();

    while( token.token == number):
        print("#store value")
        emit(0, "load", 10)
        emit(0, "add", 12)
        emit(0, "store", 1)
        emit(0, "loadv", token.value)
        emit(0, "store-ind", 0)
        getoken();
#======================================================================
# STMTLIST
# <stmtlist>  ::= <stmt> { ; <stmt> }

#===============================================================================
def stmtList():
#===============================================================================
    stmt()

    while (token.token != rightbrace):
        stmt()

    print("\n#*** Compilation finished ***\n")

#======================================================================
# STMT

#===============================================================================
def stmt():
#===============================================================================
    """  <stmt> ::=  print <expression>  |  <id> = <expression>
    """
    global codeptr

    thisStmtAdr = codeptr # Jump DEMO - not part of Stmt
    if token.token == arraysym:
        array()
    if  token.token == printsym:

        printStmt()
    if token.token == number:
        expression()

    elif  token.token == leftbracket:
       getoken()
    elif  token.token == leftbrace:
       getoken()
    elif token.token == idsym:
       assignStmt()

    elif token.token == quote:

        a =""
        getoken()

        while token.token != quote:

            a = str(a) + str(token.name)
            getoken()
        # print "string:-",a
        getoken()


    elif token.token == comment:

        b =""
        getoken()

        while token.token != '\n':

            b = str(b) + str(token.name)
            getoken()
        # print "comment:-"
        getoken()

    elif token.token == assignsym:
        getoken()
    elif token.token == ifsym:
        ifStmt()
    elif token.token == elsesym:
        elseStmt()
    elif token.token == whilesym:
        whileStmt()
    elif token.token == readint:
        readIntStmt()
    elif token.token == readch:
        readChStmt()
    elif token.token == writeint:
        writeIntStmt()
    elif token.token == writech:
        writeChStmt()
    elif  token.token == rightbracket:

        getoken()
    elif  token.token == semicolon:

        getoken()
    elif  token.token == comma:

        getoken()
    elif  token.token == lessthan:
        getoken()

        if token.token == assignsym:
            tokenNames[token.token]= "lessthanequal"
            expression()

    elif  token.token == greaterthan:
        getoken()

        if token.token == assignsym:
            tokenNames[token.token]= "greaterthanequal"
            expression()

    elif token.token == breaksym:
       print("program break")

    elif token.token == rightsquarebrace:
        print("\n#*** Compilation finished ***\n")
    else:
        error("Expected start of a statement")

    # emit (0, "return", thisStmtAdr)   Jump DEMO  need in break program

#===============================================================================
def printStmt():
#===============================================================================
    """   <printStmt> ::= print <expression>"""

    getoken()
              # skip "print"
    expression()

    emit(0, "writeint", 0)
    stmt()

    # if token.token == rightbrace:
    #     print("\n*** Compilation finished ***\n")

#===============================================================================
def assignStmt():
#===============================================================================
    """
        <id> = <expression>
    """
    whichidentifier = token

    getoken()
    # print "token>>>>",tokenNames[token.token]
    if token.token == jumpne:
        srcfile.read()
        getoken()
    elif token.token == assignsym:

        getoken()

    elif  token.token == lessthan:
        getoken()

        if token.token == assignsym:
            tokenNames[token.token]= "lessthanequal"
            print "token:-",tokenNames[token.token]


    elif  token.token == greaterthan:
        getoken()

        if token.token == assignsym:
            tokenNames[token.token]= "greaterthanequal"

    elif token.token == leftsquarebrace:

        array()
    elif token.token == jumpeq:
        getoken()
    elif token.token == rightbracket:
        getoken()
    # elif token.token == idsym: factor()
    else:
        error("Expected = in assignment statement")
    expression()
    # Save result into LHS runtime address
def ifStmt():
#if-then-else
#===============================================================================
    """   <ifStmt> ::= if <expression>"""

    getoken()                      # skip "if"
    emit(0, "compare",60)
    expression()
    stmt()
    if token.token == rightbrace: getoken()


#===============================================================================
def elseStmt():
#if-then-else
#===============================================================================
    """   <elseStmt> ::= else <expression>"""
    getoken()
    emit(0, "store", 888)
    stmt()
    emit(0, "return",0)
    if token.token == rightbrace: getoken()
#===============================================================================
def whileStmt():
#===============================================================================
    """   <whileStmt> ::= while (condition)statement"""

    getoken()

    emit(0, "compare",50)
    expression()
    stmt()
    emit(0, "increment", 1)
    emit(0, "jumpi", 14)

#===============================================================================
def readIntStmt():
#===============================================================================
    getoken()
    expression()                      # skip "if"
    emit(0, "readint",12)
#===============================================================================
def readChStmt():
#===============================================================================
    getoken()                      # skip "if"
    emit(0, "readch",11)

#===============================================================================
def writeIntStmt():
#===============================================================================
    getoken()                      # skip "if"
    expression()
    emit(0, "writeint",50)

#===============================================================================
def writeChStmt():
#===============================================================================
    getoken()                      # skip "if"
    emit(0, "writech",token.value)


#===============================================================================
def expression():
#===============================================================================
    """
       Leaves result in ACC at runtime
       Use addresses 999 & 998 as Temporary variables

       <expression> ::=  <factor> {  (+ | -)  <factor> }
    """


    # while token.token == plus or token.token == minus or token.token == mpy or token.token == div or token.token == lessthan or token.token == greaterthan or token.token == jumpne or token.token == jumpeq:
    #     op = token                        # remember +/-
    #     getoken()                         # skip past +/-
    #     emit(0, "store", 999)             # Save current result
    #     factor()
    #
    #
    # if token.token == leftbracket:
    #     getoken()
    #1st number
    if token.token == leftbracket: getoken()

    factor()

    # if  token.token == lessthan:
    #     getoken()
    #
    #     if token.token == assignsym:
    #         token.token.name = lessthanequal
    #         print "wweee",tokenNames[token.token]
    #
    #     else:
    #         ungetch()

    while token.token == plus or token.token == minus or token.token == mpy or token.token == div or token.token == lessthan or token.token == greaterthan or token.token == jumpne or token.token == jumpeq or token.token == lessthanequal or token.token == greaterthanequal:

        op = token                        # remember +/-
        getoken()
       # skip past +/-
        emit(0, "store", 997)             # Save current result
        if token.token == leftbracket:
            getoken()

        factor()

        if token.token == plus:
            emit(0, "loadv", 999)
            emit(0, "add", 999)
            emit(0, "store", 5)
        elif token.token == minus:  # Subtract - have to swap operand order
            emit(0, "store", 998)
            emit(0, "load" , 999)
            emit(0, "subtract", 998)      # Leaves result in Acc
            emit(0, "store", 6)

        if token.token == rightbracket:getoken()
        if op.token == plus:
            emit(0, "loadv", 999)
            emit(0, "add", 999)
            emit(0, "store", 5)
        if op.token == leftbracket:getoken()

        elif op.token == minus:  # Subtract - have to swap operand order
            emit(0, "store", 998)
            emit(0, "load" , 999)
            emit(0, "subtract", 998)

            emit(0, "store", 6)
            if token.token == semicolon:
                getoken()
        elif op.token == mpy:
            emit(0, "store", 998)
            emit(0, "load", 999)
            emit(0, "mpy", 998)
            emit(0, "store", 7)
        elif op.token == div:
            emit(0, "store", 998)
            emit(0, "load", 999)
            emit(0, "div", 998)
            emit(0, "store", 8)


        if token.token == plus:
            emit(0, "loadv", 999)
            emit(0, "add", 999)
            emit(0, "store", 5)
        elif token.token == minus:  # Subtract - have to swap operand order
            emit(0, "store", 998)
            emit(0, "load" , 999)
            emit(0, "subtract", 998)

            emit(0, "store", 6)
        elif op.token == mpy:
            emit(0, "store", 998)
            emit(0, "load", 999)
            emit(0, "mpy", 998)
            emit(0, "store", 7)
        elif op.token == div:
            emit(0, "store", 998)
            emit(0, "load", 999)
            emit(0, "div", 998)
            emit(0, "store", 8)
        elif op.token == lessthan:
            emit(0, "store", 998)
            emit(0, "compare", 999)
            emit(0, "jumplt", 80)
            emit(0, "loadv", 0)
            emit(0, "store", 9)
        elif op.token == greaterthan:
            emit(0, "store", 998)
            emit(0, "compare", 999)
            emit(0, "jumpgt", 81)
            emit(0, "loadv", 0)
            emit(0, "store", 10)

#not working
        elif op.token == jumpne:
            emit(0, "store", 998)
            emit(0, "compare", 999)
            emit(0, "jumpne", 82)
            emit(0, "loadv", 0)
            emit(0, "store", 11)

        elif op.token == jumpeq:
            emit(0, "store", 998)
            emit(0, "compare", 999)
            emit(0, "jumpeq", 83)


#===============================================================================
def factor():
#===============================================================================
    """
      # FACTOR() - leaves result in ACC at runtime
          <factor> ::= identifier | number
    """

    if token.token == plus:
        getoken()
        #- (unary)
    elif token.token == minus:
        getoken()

    if token.token == idsym:
        emit(0, "load",  token.address)

        getoken()
    elif token.token == comment:
        b =""
        getoken()

        while token.token != "\\n":

            b = str(b) + str(token.name)
            getoken()
        print "comment:-",b
        getoken()

    elif token.token == quote:

        a =""
        getoken()

        while token.token != quote:

            a = str(a) + str(token.name)
            getoken()
        # print "string:-",a
        getoken()

    elif  token.token == lessthan:
        getoken()

        if token.token == assignsym:
            tokenNames[token.token]= "lessthanequal"


    elif  token.token == greaterthan:
        getoken()

        if token.token == assignsym:
            tokenNames[token.token]= "greaterthanequal"


    elif token.token == number:
        emit(0, "loadv", token.value)
        # emit(0, "store", 1)
        getoken()
    # elif token.token == assignsym:
    #     getoken()
    elif token.token == leftbracket:

        getoken()
        # print "this is token",tokenNames[token.token]
    elif token.token == leftsquarebrace: getoken()

    elif  token.token == rightbracket:

        getoken()
    elif  token.token == rightsquarebrace:

        getoken()
    elif  token.token == semicolon:
        getoken()

    elif  token.token == leftbrace:
       getoken()

    elif token.token == rightbrace:
         print("\n#*** Compilation finished ***\n")
    elif token.token == breaksym:
       print("program break")

    else:
        error("Start Of Factor Expected")


#===============================================================================
def program():
#===============================================================================
    """ # PROGRAM - Start production of the grammar
        #  <program> ::= { <id> ; <vars > <stmtlist>  }
    """
    if token.token == programsym:
        # print "name",tokenNames[token.token]
        getoken()
    else: error("Program start with 'program' keyword")

    if token.token == idsym:
        getoken()

    else: error("Program name expected")

    if token.token == semicolon:
        getoken()

    else: error("Semicolon expected")

    if token.token == declaresym :

        vars()
    if token.token == varsym :
        vars()

    stmtList()

    if token.token == rightbrace:
        print("#\n*** Compilation finished ***\n")


    else: error(" } expected")

# Compiler main
#======================================================================
def main():
#======================================================================
    global line
    global srcfile
    global charIndex

    initSymTbl()


    with open("break.txt") as line1:
        out1file = open('output.obj','w')
        line = line1.read()
       #   print line

    charIndex = 0

    debugScanner = False
    if debugScanner:   # Scanner Test Routine - Read a line and display the tokens
       getoken()
       while token.token is not None:   # Skeleton for testing Scanner
           printToken(token)
           y = str(token)
           out1file.write(y)
           getoken()
       sys.exit(1)
    else:
        getoken()                       # Parse Program according to the grammar
        program()                       # Call start non-terminal handler



# emit(0, "load",  0)
# emit(0, "constant",  "'A'")                   #need in break,comment,exitloop,writer-if-read-write-ch   program
# emit(0, "constant",  10)
# emit(0, "constant",  12)
# emit(0, "constant",  25)
# emit(0, "constant",  -10)

# outfile = open('constant.obj', "w")
#
# z = str("#Microcompiler.py v0.2")
# outfile.write(z)
#
# emit(0, "load",  0)
# z1 = str('emit(0, "load",  0)')
# outfile.write(z1)
#
# emit(0, "constant",  "'A'")
# z2 = str('emit(0, "constant",  "A")')
# outfile.write(z2)
#
# emit(0, "constant",  10)
# z3 = str('emit(0, "constant",  10)')
# outfile.write(z3)
#
# z4=emit(0, "constant",  12)
# z4 = str('emit(0, "constant",  12)')
# outfile.write(z4)
#
# z5=emit(0, "constant",  25)
# z5 = str('emit(0, "constant",  25)')
# outfile.write(z5)
#
# z6=emit(0, "constant",  -10)
# z6 = str('emit(0, "constant",  -10)')
# outfile.write(z6)
# outfile.close()


main()
getoken()


#=======================================================================
# *** That's it folks - written by Giovanni Moretti - April 27, 2011 ***
#=======================================================================

